package personal.parser;

import personal.util.IdAllocter;
import personal.util.Map2D;
import personal.util.Pair;

import java.util.*;

public class SimpleParserFA implements ParserFA {
    /**
     * 构造函数，传入对应的文法
     *
     * @param grammar 对应的文法
     */
    public SimpleParserFA(Grammar grammar) {
        // 初始化数据结构
        this.nodeIdAllocter = new IdAllocter<>(1);
        this.symbolIdAllocter = new IdAllocter<>(1);
        this.grammar = grammar;
        this.adj = new Map2D<>();
        // 初始化自动机
        SimpleStatus StartNode = new SimpleStatus();
        StartNode.add(new SimpleItem(this.grammar.getProductions().get(0), new HashSet<>(List.of((Grammar.Terminal) this.grammar.getSymbol("$")))));
        this.indexOf(StartNode.toClosure(this.grammar)); // 为第一个节点编号
        // 开始转移
        Queue<SimpleStatus> qe = new LinkedList<>();
        this.nodeIdAllocter.pollAllTo(qe);
        while (!qe.isEmpty()) {
            SimpleStatus node = qe.poll();
            node.initTag(); // 初始化 tag
            this.transitForEachSymbol(node);
            this.nodeIdAllocter.pollAllTo(qe);
        }
    }

    /**
     * 遍历并处理指定节点的所有转移
     *
     * @param nodeU 指定节点
     */
    private void transitForEachSymbol(SimpleStatus nodeU) {
        for (Grammar.Symbol transit : nodeU.getCurrents()) {
            SimpleStatus nodeV = this.transitBySymbol(nodeU, transit);
            int idU = this.indexOf(nodeU), idV = this.indexOf(nodeV), idW = this.indexOf(transit);
            if (!this.addEdge(idU, idV, idW)) throw new UnknownError("转移节点时，发生未知错误");
        }
    }

    /**
     * 求指定节点进行转移后的节点
     *
     * @param init   指定节点
     * @param symbol 转移符号
     * @return 转移后的节点
     */
    private SimpleStatus transitBySymbol(SimpleStatus init, Grammar.Symbol symbol) {
        SimpleStatus ret = new SimpleStatus();
        List<SimpleItem> items = init.stream().filter(i -> symbol.equals(i.getCurrentOrNull())).toList(); // 取出当前符号是 symbol 的产生式
        for (SimpleItem item : items) { // 遍历 Status 中的 item
            ret.add(item.getNextItem());
        }
        return ret.toClosure(this.grammar);
    }

    /**
     * 添加转移边
     *
     * @param idU       起点的编号
     * @param idV       终点的编号
     * @param idTransit 转移符号的编号
     * @return 是否添加成功
     */
    private boolean addEdge(int idU, int idV, int idTransit) {
        return this.adj.putIfAbsent(idU, idTransit, idV) == null;
    }

    /**
     * 转移表
     */
    private final Map2D<Integer, Integer, Integer> adj; // <u, w, v>

    /**
     * 为节点编号
     */
    private final IdAllocter<SimpleStatus> nodeIdAllocter;


    /**
     * 为转移编号
     */
    private final IdAllocter<Grammar.Symbol> symbolIdAllocter;


    /**
     * 文法
     */
    private final Grammar grammar;

    @Override
    public int size() {
        return this.nodeIdAllocter.size();
    }

    @Override
    public Status getStatus(int id) {
        return this.nodeIdAllocter.getObject(id);
    }

    @Override
    public int indexOf(Status node) {
        return this.nodeIdAllocter.getId((SimpleStatus) node);
    }

    @Override
    public Grammar.Symbol getSymbol(int id) {
        return this.symbolIdAllocter.getObject(id);
    }

    @Override
    public int indexOf(Grammar.Symbol symbol) {
        return this.symbolIdAllocter.getId(symbol);
    }

    @Override
    public Status transitTo(Status u, Grammar.Symbol transit) throws NullPointerException {
        return this.getStatus(this.adj.get(this.indexOf(u), this.indexOf(transit)));
    }

    @Override
    public int transitTo(int u, Grammar.Symbol transit) throws NullPointerException {
        return this.adj.get(u, this.indexOf(transit));
    }


    @Override
    public String toString() {
        StringBuilder ret = new StringBuilder();
        ret.append("```mermaid\n").append("flowchart LR\n");
        for (int i = 1; i <= this.size(); ++i) {
            ret.append(String.format("n%d[\"n%d\n%s\"]\n", i, i, this.getStatus(i)));
        }
        for (Map.Entry<Pair<Integer, Integer>, Integer> it1 : this.adj.entrySet()) {
            int u = it1.getKey().getFirst(), w = it1.getKey().getSecond(), v = it1.getValue();
            ret.append(String.format("n%d--\"%s\"-->n%d\n", u, this.getSymbol(w), v));
        }
        ret.append("```\n");
        return ret.toString();
    }

}
